﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace AOC19NET.Classes {
	/// <summary>
	/// This class will serve as an interface to access some performance values available directly from Windows.
	/// The CPU, RAM and network statistics can be obtained with this class, avoiding the need of having RivaTuner running.
	/// The methods in this class will be static, so we don't need to create any instance to access them.
	/// </summary>
	class CWindowsPerformanceUtils {
		/*-------------------------------------------------------------------------*/
		/* DEFINITIONS															   */
		/*-------------------------------------------------------------------------*/
		#region DEFINITIONS
			/// <summary>
			/// Needed stuff to obtain the physical memory.
			/// </summary>
			[StructLayout(LayoutKind.Sequential)]
			private struct MEMORYSTATUSEX {
				public int length;
				public int memoryLoad;
				public ulong totalPhys;
				public ulong availPhys;
				public ulong totalPageFile;
				public ulong availPageFile;
				public ulong totalVirtual;
				public ulong availVirtual;
				public ulong availExtendedVirtual;
			}
			[DllImport("kernel32.dll", SetLastError=true)]
			private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX buffer);
		#endregion

		/*-------------------------------------------------------------------------*/
		/* MEMBERS																   */
		/*-------------------------------------------------------------------------*/
		#region MEMBERS
			/// <summary>
			/// We'll keep a PerformanceCounter for each CPU in the computer.
			/// </summary>
			protected List<PerformanceCounter> m_vCPUCounters;

			/// <summary>
			/// We'll keep a PerformanceCounter for each Network adapter in the
			/// computer, both for in and out traffic.
			/// </summary>
			protected List<PerformanceCounter> m_vNetInCounters;
			protected List<PerformanceCounter> m_vNetOutCounters;
		#endregion

		/*-------------------------------------------------------------------------*/
		/* METHODS																   */
		/*-------------------------------------------------------------------------*/
		#region METHODS
			/// <summary>
			/// Default constructor.
			/// </summary>
			public CWindowsPerformanceUtils() {
				m_vCPUCounters = new List<PerformanceCounter>();
				m_vNetInCounters = new List<PerformanceCounter>();
				m_vNetOutCounters = new List<PerformanceCounter>();

				// Check how many CPU has the computer and initialize a counter for each one.
				for(int i = 0; i < Environment.ProcessorCount; i++) {
					m_vCPUCounters.Add(new PerformanceCounter("Processor", "% Processor Time", i.ToString()));
				}

				InitNetworkCounters();
			}

			/// <summary>
			/// Returns the total amount of memory in the system (in MB).
			/// </summary>
			/// <returns>The total amount of physical memory in the system, in MB.</returns>
			public ulong GetTotalMemoryMB() {
				MEMORYSTATUSEX status = new MEMORYSTATUSEX();
				status.length = Marshal.SizeOf(status);
				if(!GlobalMemoryStatusEx(ref status)) {
					int err = Marshal.GetLastWin32Error();
					throw new Win32Exception(err);
				}
				return status.totalPhys/1024/1024;		// That makes Mb
			}

			/// <summary>
			/// Returns the available amount of memory in the system (in MB).
			/// </summary>
			/// <returns>The available amount of physical memory in the system, in MB.</returns>
			public ulong GetAvailableMemoryMB() {
				MEMORYSTATUSEX status = new MEMORYSTATUSEX();
				status.length = Marshal.SizeOf(status);
				if(!GlobalMemoryStatusEx(ref status)) {
					int err = Marshal.GetLastWin32Error();
					throw new Win32Exception(err);
				}
				return status.availPhys/1024/1024;		// That makes Mb
			}

			/// <summary>
			/// Returns the available amount of memory in the system (in %).
			/// </summary>
			/// <returns>The available amount of physical memory in the system, in %.</returns>
			public float GetAvailableMemoryPercentage() {
				MEMORYSTATUSEX status = new MEMORYSTATUSEX();
				status.length = Marshal.SizeOf(status);
				if(!GlobalMemoryStatusEx(ref status)) {
					int err = Marshal.GetLastWin32Error();
					throw new Win32Exception(err);
				}
				return (float)status.availPhys/(float)status.totalPhys * 100.0f;		// That makes Mb
			}

			/// <summary>
			/// Returns the current usage of an specific CPU.
			/// </summary>
			/// <param name="_iCpu">The index of the CPU to analyze.</param>
			/// <returns>The current usage in %.</returns>
			public float GetCPUUsage(int _iCpu) {
				if(_iCpu < 0 || _iCpu >= Environment.ProcessorCount) {
					_iCpu = 0;
				}

				// Performance counter to do the measure
				return m_vCPUCounters[_iCpu].NextValue();
			}

			/// <summary>
			/// Returns the amount of incoming traffic in the system, in KBytes/sec.
			/// If more than one network interface is present, the return value is the sum
			/// of the incoming traffic for each device.
			/// </summary>
			/// <returns>Total amount of incoming traffic in KBytes/sec.</returns>
			public float GetNetInTraffic() {
				// Loop through all the interfaces and read the incoming data
				float fTotalKB = 0;
				for(int i = 0; i < m_vNetInCounters.Count; i++) {
					fTotalKB += m_vNetInCounters[i].NextValue() / 1024.0f;
				}

				return fTotalKB;
			}

			/// <summary>
			/// Returns the amount of outgoing traffic in the system, in KBytes/sec.
			/// If more than one network interface is present, the return value is the sum
			/// of the outgoing traffic for each device.
			/// </summary>
			/// <returns>Total amount of outgoing traffic in KBytes/sec.</returns>
			public float GetNetOutTraffic() {
				// Loop through all the interfaces and read the incoming data
				float fTotalKB = 0;
				for(int i = 0; i < m_vNetOutCounters.Count; i++) {
					fTotalKB += m_vNetOutCounters[i].NextValue() / 1024.0f;
				}

				return fTotalKB;
			}

			/// <summary>
			/// Initializes an IN and an OUT performance counter for each network adapter
			/// in the system.
			/// </summary>
			private void InitNetworkCounters() {
				// Loop through all the newtork adapters
				PerformanceCounterCategory NetworkCategory = new PerformanceCounterCategory("Network Interface");
				foreach(String sName in NetworkCategory.GetInstanceNames()) {
					// This one exists in every computer, skip it
					if(sName == "MS TCP Loopback interface") {
						continue;
					}

					// Add an IN traffic counter to the list
					PerformanceCounter NewInCounter = new PerformanceCounter("Network Interface", "Bytes Received/sec", sName);
					m_vNetInCounters.Add(NewInCounter);

					// Add an OUT traffic counter to the list
					PerformanceCounter NewOutCounter = new PerformanceCounter("Network Interface", "Bytes Sent/sec", sName);
					m_vNetOutCounters.Add(NewOutCounter);
				}
			}
		#endregion
	}
}
